using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Globalization;
using Axe.Logging;

namespace Axe.Workflow
{
    public delegate void ExecutionStateChanged();
    public delegate bool ExecutionStateChanging();

    public abstract class Task : ITask
    {
        object SyncRoot = new object();
        ExecutionControllerState m_State = ExecutionControllerState.Stopped;
        ExecutionControllerState m_OldState = ExecutionControllerState.Stopped;

        void SaveState() { m_OldState = m_State; }
        void ResetState() { m_State = m_OldState; }

        List<ITask> mDependentControllers = new List<ITask>();
        string m_TaskName = "";

        ControllerStateChangeStyle mStartStyle = ControllerStateChangeStyle.DependentFirst;
        ControllerStateChangeStyle mStopStyle = ControllerStateChangeStyle.DependentFirst;
        ControllerStateChangeStyle mSuspendStyle = ControllerStateChangeStyle.DependentFirst;
        ControllerStateChangeStyle mResumeStyle = ControllerStateChangeStyle.DependentFirst;

        #region public void SetStateChangeStyle(ExecutionControllerCommand cmd, ControllerStateChangeStyle style)
        /// <summary>
        /// Sets the change style of task and it's child tasks state.
        /// </summary>
        /// <param name="cmd">The task command.</param>
        /// <param name="style">The style, determining the way to change task state.</param>
        public void SetStateChangeStyle(ExecutionControllerCommand cmd, ControllerStateChangeStyle style)
        {
            switch(cmd)
            {
                case ExecutionControllerCommand.Start:
                    mStartStyle = style;
                    break;

                case ExecutionControllerCommand.Stop:
                    mStopStyle = style;
                    break;

                case ExecutionControllerCommand.Suspend:
                    mSuspendStyle = style;
                    break;

                case ExecutionControllerCommand.Resume:
                    mResumeStyle = style;
                    break;
            }
        }
        #endregion

        #region public Task(string taskName)
        /// <summary>
        /// Initializes a new instance of the <see cref="Task"/> class.
        /// </summary>
        /// <param name="taskName">ObjectName of the task.</param>
        public Task(string taskName)
        {
            TaskName = taskName;
            OnStart += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnStop += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnSuspend += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnResume += new ExecutionStateChanged(OnStateChangedEventHandler);
        }
        #endregion

        #region public Task()
        /// <summary>
        /// Initializes a new instance of the <see cref="Task"/> class.
        /// </summary>
        public Task()
        {
            OnStart += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnStop += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnSuspend += new ExecutionStateChanged(OnStateChangedEventHandler);
            OnResume += new ExecutionStateChanged(OnStateChangedEventHandler);
        }
        #endregion

        #region private void OnStateChangedEventHandler()
        /// <summary>
        /// Called when task newState has changed.
        /// </summary>
        private void OnStateChangedEventHandler()
        {
            if (this.SupportsLogging)
            {
                LogEntry Entry = new LogEntry(LogEntryType.Info, DateTime.Now);

                Entry["TaskTame"] = TaskName.ToString();
                Entry["TaskState"] = TaskState.ToString();

                this.GetLoggingProvider().Add(Entry);
            }
        }
        #endregion

        #region private bool DelegateCmdToChilds(ExecutionControllerCommand cmd)
        /// <summary>
        /// Delegates the control command to child tasks.
        /// </summary>
        /// <param name="cmd">The command.</param>
        /// <returns>
        /// <c>true</c>, if all child tasks executed specified command successfully;
        /// otherwise, <c>false</c>.
        /// </returns>
        private bool DelegateCmdToChilds(ExecutionControllerCommand cmd)
        {
            bool WasError = false;

            lock(mDependentControllers)
            {
                foreach(ITask ctrl in mDependentControllers)
                {
                    if(Task.ExecuteControllerCommand(ctrl, cmd) == false)
                    {
                        WasError = true;

                        if(this.SupportsLogging)
                        {
                            ILogger provider = this.GetLoggingProvider();

                            LogEntry entry = new LogEntry(LogEntryType.Error, DateTime.Now);

                            entry.Text = "Dependent task failed to execute command";

                            entry["Root Task"] = this.TaskName;
                            entry["Child Task"] = ctrl.TaskName;
                            entry["Command"] = cmd.ToString();
                            entry["Current state"] = ctrl.TaskState.ToString();

                            provider.Add(entry);
                        }
                    }
                }
            }

            return !WasError;
        }

        protected virtual bool SupportsLogging
        {
            get { return false; }
        }

        protected virtual ILogger GetLoggingProvider()
        {
            throw new NotImplementedException();
        }
        #endregion

        #region Dependent controllers management
        /// <summary>
        /// Adds the dependent task and set it's newState to the caller task newState.
        /// </summary>
        /// <param name="ctrl">The CTRL.</param>
        public void AddDependentTask(ITask ctrl)
        {
            lock(mDependentControllers)
            {
                mDependentControllers.Add(ctrl);
                SetTaskState(ctrl, TaskState);
            }
        }

        public void RemoveDependent(ITask ctrl)
        {
            lock(mDependentControllers)
            {
                /*
                ctrl.Stop();
                */
                
                while(mDependentControllers.Remove(ctrl))
                {
                    
                };
            }
        }

        public void ClearDependenceList()
        {
            lock(mDependentControllers)
            {
                mDependentControllers.Clear();
            }
        }
        #endregion

        protected event ExecutionStateChanging OnStarting;
        protected event ExecutionStateChanging OnStopping;
        protected event ExecutionStateChanging OnSuspending;
        protected event ExecutionStateChanging OnResuming;

        public event ExecutionStateChanged OnStart;
        public event ExecutionStateChanged OnStop;
        public event ExecutionStateChanged OnSuspend;
        public event ExecutionStateChanged OnResume;

        #region ITask Members

        #region public string TaskName
        public string TaskName
        {
            get
            {
                return m_TaskName;
            }
            set
            {
                m_TaskName = value;
            }
        }
        #endregion

        #region public ExecutionControllerState TaskState
        /// <summary>
        /// Gets the newState of the current task.
        /// </summary>
        /// <value>The newState of the task.</value>
        public ExecutionControllerState TaskState
        {
            get
            {
                return m_State;
            }
            private set
            {
                m_OldState = m_State;
                m_State = value;
            }
        }
        #endregion

        #region public bool IsRunning
        /// <summary>
        /// Gets a value indicating whether this service is running.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        public bool IsRunning
        {
            get
            {
                return m_State == ExecutionControllerState.Running;
            }
        }
        #endregion

        #region public bool IsPaused
        /// <summary>
        /// Gets a value indicating whether this service is paused.
        /// </summary>
        /// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value>
        public bool IsPaused
        {
            get
            {
                return m_State == ExecutionControllerState.Paused;
            }
        }

        #endregion

        #region static bool IsMulticastCallSuccess(ExecutionStateChanging deleg)
        static bool IsMulticastCallSuccess(ExecutionStateChanging deleg)
        {
            if (deleg == null)
                return true;

            return IsCallSuccess(Utility.ConvertArray<Delegate, ExecutionStateChanging>(deleg.GetInvocationList()));
        } 
        #endregion

        #region static bool IsCallSuccess(ExecutionStateChanging[] delegs)
        static bool IsCallSuccess(ExecutionStateChanging[] delegs)
        {
            if (delegs == null)
                return true;

            bool wasCallError = false;
            foreach (ExecutionStateChanging d in delegs)
            {
                if (IsCallSuccess(d, false) == false)
                {
                    wasCallError = true;
                }
            }

            return !wasCallError;
        } 
        #endregion

        #region static bool IsCallSuccess(ExecutionStateChanging deleg, bool defaultValue)
        static bool IsCallSuccess(ExecutionStateChanging deleg, bool defaultValue)
        {
            bool res = defaultValue;

            if (deleg == null)
                return res;

            try
            {
                res = deleg();
            }
            catch
            {
                res = defaultValue;
            }

            return res;
        } 
        #endregion

        public bool IsChildTasksStateMatch
        {
            get
            {
                if (mDependentControllers.Count == 0)
                {
                    return true;
                }
                else
                {
                    lock (SyncRoot)
                    {
                        foreach (Task task in mDependentControllers)
                        {
                            if (TaskState == task.TaskState)
                                return false;

                            if (task.IsChildTasksStateMatch == false)
                                return false;
                        }
                    }

                    return true;
                }
            }
        }
        #region public bool Start()
        /// <summary>
        /// Starts execution of current task and all it's child tasks. 
        /// </summary>
        /// <returns><c>true</c> if task was started successfully;
        /// otherwise - <c>false</c>.</returns>
        public bool Start()
        {
            bool childsStarted;

            lock(SyncRoot)
            {
                if (!IsRunning && !IsPaused)
                {
                    switch (mStartStyle)
                    {
                        case ControllerStateChangeStyle.CallerFirst:
                            {
                                TaskState = ExecutionControllerState.Running;

                                if (!IsMulticastCallSuccess(OnStarting))
                                {
                                    ResetState();
                                    return false;
                                }

                                childsStarted = DelegateCmdToChilds(ExecutionControllerCommand.Start);

                                if (childsStarted == false)
                                {
                                    ResetState();
                                    return false;
                                }

                                SaveState();

                                if (OnStart != null)
                                    OnStart();

                                return true;
                            }
                        case ControllerStateChangeStyle.DependentFirst:
                            {
                                childsStarted = DelegateCmdToChilds(ExecutionControllerCommand.Start);

                                if (childsStarted == false)
                                    return false;

                                TaskState = ExecutionControllerState.Running;

                                if (!IsMulticastCallSuccess(OnStarting))
                                {
                                    ResetState();
                                    return false;
                                }

                                SaveState();

                                if (OnStart != null)
                                    OnStart();

                                return true;
                            }

                        default:
                            return false;
                    }
                }
                else
                {
                    if (!IsChildTasksStateMatch)
                    {
                        return DelegateCmdToChilds(ExecutionControllerCommand.Start);
                    }

                    return true;
                }
            }
        }
        #endregion

        #region public bool Stop()
        /// <summary>
        /// Stops execution of current task and all it's subtasks.
        /// </summary>
        public bool Stop()
        {
            bool childsStopped;

            lock(SyncRoot)
            {
                if (IsRunning)
                {
                    switch (mStopStyle)
                    {
                        case ControllerStateChangeStyle.CallerFirst:
                            {
                                TaskState = ExecutionControllerState.Stopped;

                                if (!IsMulticastCallSuccess(OnStopping))
                                {
                                    ResetState();
                                    return false;
                                }

                                childsStopped = DelegateCmdToChilds(ExecutionControllerCommand.Stop);

                                if (childsStopped == false)
                                {
                                    ResetState();
                                    return false;
                                }

                                SaveState();
                                if (OnStop != null)
                                    OnStop();

                                return true;
                            }
                        case ControllerStateChangeStyle.DependentFirst:
                            {
                                childsStopped = DelegateCmdToChilds(ExecutionControllerCommand.Stop);

                                if (childsStopped == false)
                                    return false;

                                TaskState = ExecutionControllerState.Stopped;

                                if (!IsMulticastCallSuccess(OnStopping))
                                {
                                    ResetState();
                                    return false;
                                }

                                SaveState();

                                if (OnStop != null)
                                    OnStop();

                                return true;
                            }
                        default:
                            return false;
                    }
                }
                else
                {
                    if (IsChildTasksStateMatch == false)
                    {
                        return DelegateCmdToChilds(ExecutionControllerCommand.Stop);
                    }

                    return true;
                }
            }
        }
        #endregion

        #region public bool Suspend()
        /// <summary>
        /// Suspends execution of current task and all it's subtasks
        /// </summary>
        public bool Suspend()
        {
            if(!IsRunning)
                return false;

            bool childsSuspend;

            lock(SyncRoot)
            {
                switch(mSuspendStyle)
                {
                    case ControllerStateChangeStyle.CallerFirst:
                        {
                            if(OnSuspending != null)
                                if(OnSuspending() == false)
                                    return false;

                            childsSuspend = DelegateCmdToChilds(ExecutionControllerCommand.Suspend);

                            if(childsSuspend == false)
                                return false;

                            m_State = ExecutionControllerState.Paused;

                            if(OnSuspend != null)
                                OnSuspend();

                            return true;
                        }
                    case ControllerStateChangeStyle.DependentFirst:
                        {
                            childsSuspend = DelegateCmdToChilds(ExecutionControllerCommand.Suspend);
                            if(childsSuspend == false)
                                return false;

                            if(OnSuspending != null)
                                if(OnSuspending() == false)
                                    return false;

                            m_State = ExecutionControllerState.Paused;

                            if(OnSuspend != null)
                                OnSuspend();

                            return true;
                        }
                    default:
                        return false;
                }
            }
        }
        #endregion

        #region public bool Resume()
        /// <summary>
        /// Resumes execution of current task and all it's subtasks.
        /// </summary>
        public bool Resume()
        {
            if(!IsPaused)
                return false;

            bool childsResumed;
            lock(SyncRoot)
            {
                switch(mResumeStyle)
                {
                    case ControllerStateChangeStyle.CallerFirst:
                        {
                            if (!IsMulticastCallSuccess(OnResuming))
                                return false;

                            childsResumed = DelegateCmdToChilds(ExecutionControllerCommand.Resume);

                            if(childsResumed == false)
                                return false;

                            m_State = ExecutionControllerState.Running;

                            if(OnResume != null)
                                OnResume();

                            return true;
                        }
                    case ControllerStateChangeStyle.DependentFirst:
                        {
                            childsResumed = DelegateCmdToChilds(ExecutionControllerCommand.Resume);
                            if(childsResumed == false)
                                return false;

                            if (!IsMulticastCallSuccess(OnResuming))
                                return false;

                            m_State = ExecutionControllerState.Running;

                            if(OnResume != null)
                                OnResume();

                            return true;
                        }
                    default:
                        return false;
                }
            }
        }
        #endregion

        #endregion

        #region public override string ToString()
        /// <summary>
        /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
        /// </returns>
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(String.Format("{0} [{1}]",TaskName,TaskState));

            if (mDependentControllers.Count > 0)
            {
                foreach (Task task in mDependentControllers)
                {
                    string taskname = task.ToString();
                    string[] strs = taskname.Split(new string[]{Environment.NewLine},StringSplitOptions.RemoveEmptyEntries);

                    foreach (string s in strs)
                    {
                        sb.AppendLine(s.PadLeft(s.Length + 1));
                    }
                }
            }

            return sb.ToString();
        }
        #endregion

        #region public static void SetTaskState(ITask ctrl, ExecutionControllerState newState)
        /// <summary>
        /// Requests specified task to change it's newState to specified.
        /// </summary>
        /// <param name="ctrl">The task.</param>
        /// <param name="newState">The requested newState.</param>
        /// <returns>The result of newState change operation.</returns>
        public static bool SetTaskState(ITask ctrl, ExecutionControllerState newState)
        {
            if(ctrl == null)
                return false;

            ExecutionControllerState oldState = ctrl.TaskState;

            if(oldState == newState)
                return true;

            switch(newState)
            {
                case ExecutionControllerState.Stopped:
                    if(ctrl.IsRunning || ctrl.IsPaused)
                        return ctrl.Stop();
                    break;

                case ExecutionControllerState.Paused:
                    if(ctrl.IsRunning)
                        return ctrl.Suspend();
                    break;

                case ExecutionControllerState.Running:
                    if(ctrl.IsPaused)
                        return ctrl.Resume();
                    else if(!ctrl.IsRunning)
                        return ctrl.Start();
                    break;

                default:
                    return false;
            }

            return false;
        }
        #endregion

        #region public static void ExecuteControllerCommand(ITask ctrl, ExecutionControllerCommand cmd)
        /// <summary>
        /// Executes the controller command.
        /// </summary>
        /// <param name="ctrl">The task.</param>
        /// <param name="cmd">The command to execute.</param>
        /// <returns>The result of the operation.</returns>
        private static bool ExecuteControllerCommand(ITask ctrl, ExecutionControllerCommand cmd)
        {
            switch(cmd)
            {
                case ExecutionControllerCommand.Start:
                    return ctrl.Start();

                case ExecutionControllerCommand.Stop:
                    return ctrl.Stop();

                case ExecutionControllerCommand.Suspend:
                    return ctrl.Suspend();

                case ExecutionControllerCommand.Resume:
                    return ctrl.Resume();

                default:
                    return false;
            }
        }
        #endregion
    };
}
